using System;
using System.Collections.Generic;
using Unity.Collections;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
public struct VLightData
{
    public int mainLightIndex;
    
    public NativeArray<VisibleLight> visibleLights;
    public int additionalLightsCount;
    public bool supportsAdditionalLights;
}
public struct VRenderingData
{
    public CullingResults cullResults;
    public VCameraData cameraData;
    public VLightData lightData;
    public ShadowSetting shadowSetting;
    public bool postProcessingEnabled;
    public PostFXSettings postFXSettings;
    //For TAA
    public uint    FrameCount;
    public Vector2 ProjectionJitter;
    public Vector2 NextProjectionJitter;
    public Matrix4x4 JitteredProjectionMatrix;
    public Matrix4x4 ProjectionMatrix;
    public Matrix4x4 ViewMatrix;
}

public struct VCameraData
{
    public VScriptableRenderer renderer;
    public RenderTextureDescriptor cameraTargetDescriptor;
    public Camera camera;
    public Matrix4x4 Inv_Projection;

}

public abstract class VScriptableRenderer : IDisposable
{
    internal static class ShaderPropertyId
    {

        public static readonly int inverseViewMatrix = Shader.PropertyToID("unity_MatrixInvV");
        public static readonly int inverseProjectionMatrix = Shader.PropertyToID("unity_MatrixInvP");
        public static readonly int inverseViewAndProjectionMatrix = Shader.PropertyToID("unity_MatrixInvVP");

        // public static readonly int cameraProjectionMatrix = Shader.PropertyToID("unity_CameraProjection");
        // public static readonly int inverseCameraProjectionMatrix = Shader.PropertyToID("unity_CameraInvProjection");
        public static readonly int worldToCameraMatrix = Shader.PropertyToID("unity_WorldToCamera");
        public static readonly int cameraToWorldMatrix = Shader.PropertyToID("unity_CameraToWorld");

    }
    protected List<VScriptableRenderPass> m_ActiveRenderPassQueue = new List<VScriptableRenderPass>(32);
    protected RenderTargetIdentifier  m_ActiveCameraColorAttachment;
    protected RenderTargetIdentifier  m_ActiveCameraDepthAttachment;
    //public abstract VScriptableRenderer Create();
    public abstract void Setup(ScriptableRenderContext context, ref VRenderingData renderingData);
    RenderTargetIdentifier m_CameraColorTarget;
    RenderTargetIdentifier m_CameraDepthTarget;
    public void ConfigureCameraTarget(RenderTargetIdentifier colorTarget, RenderTargetIdentifier depthTarget)
    {
        m_CameraColorTarget = colorTarget;
        m_CameraDepthTarget = depthTarget;
    }
    internal void ConfigureCameraColorTarget(RenderTargetIdentifier colorTarget)
    {
        m_CameraColorTarget = colorTarget;
    }
    public virtual void Execute(ScriptableRenderContext context, ref VRenderingData renderingData)
    {
        // ref var cameraData = ref renderingData.cameraData;
        // Debug.Log("CustomScriptableRenderer Execute, pass queue count: " + m_ActiveRenderPassQueue.Count);
        // {
        //     CommandBuffer cmd = CommandBufferPool.Get();
        //     
        //     cmd.ClearRenderTarget(true,true,Color.black,1);
        //     
        //     foreach (var pass in m_ActiveRenderPassQueue)
        //     {
        //         pass.OnCameraSetup(cmd,ref renderingData);
        //     }
        //     context.ExecuteCommandBuffer(cmd);
        //     CommandBufferPool.Release(cmd);
        //     // Sort the render pass queue
        //     // SortStable(m_ActiveRenderPassQueue);
        // }
        // {
        //     //using var renderBlocks = new RenderBlocks(m_ActiveRenderPassQueue);
        //     //ready to Execute
        // }
        // {
        //     //config renderTarget
        //     
        //     CommandBuffer cmd = CommandBufferPool.Get();
        //     
        //     
        //     
        //     foreach (var pass in m_ActiveRenderPassQueue)
        //     {
        //         
        //         pass.Configure(cmd,cameraData.cameraTargetDescriptor);
        //         SetRenderPassAttachments(cmd, pass,ref cameraData);
        //     }
        //     
        //     context.ExecuteCommandBuffer(cmd);
        //     
        //     CommandBufferPool.Release(cmd);
        // }
        // foreach (var pass in m_ActiveRenderPassQueue)
        // {
        //     CommandBuffer cmd = CommandBufferPool.Get();
        //     cmd.SetRenderTarget(m_ActiveCameraColorAttachment,m_ActiveCameraDepthAttachment);
        //     context.ExecuteCommandBuffer(cmd);
        //     CommandBufferPool.Release(cmd);
        //     pass.Execute(context, ref renderingData);
        // }
        //
        // {
        //     CommandBuffer cmd = CommandBufferPool.Get();
        //     foreach (var pass in m_ActiveRenderPassQueue)
        //     {
        //         pass.OnCameraCleanup(cmd);
        //     }
        //     
        //     context.ExecuteCommandBuffer(cmd);
        //     CommandBufferPool.Release(cmd);
        //     
        // }
        
    }

    public void EnqueuePass(VScriptableRenderPass pass)
    {
        m_ActiveRenderPassQueue.Add(pass);
    }

    public virtual void Dispose()
    {
        
    }


    void SetRenderPassAttachments(CommandBuffer cmd, VScriptableRenderPass renderPass, ref VCameraData cameraData)
    {
        Camera camera = cameraData.camera;
        
    }
}

public class VForwardRenderer : VScriptableRenderer
{
    private static readonly ProfilingSampler m_ProfilingRenderPass = 
        new ProfilingSampler("666");
    
    static int frameBufferId = Shader.PropertyToID("ColorTexture");
    static int tempBufferId = Shader.PropertyToID("tempColorTexture");
    static int DepthBufferId = Shader.PropertyToID("DepthTexture");
    private RenderTargetIdentifier m_tempAttachment;
    private VDrawSkyboxPass m_DrawSkyboxPass;
    private VDrawObjectPass m_DrawObjectPass;
    //private VDepthOnlyPass m_DepthOnlyPass;
    private VShadowCasterPass m_ShadowCasterPass;
    private VDepthOnlyPass m_DepthOnlyPass;
    private VForwardLights m_ForwardLights=new VForwardLights();
    private VBlitPass m_blitPass;
    private VTAAPass m_TAAPass;
    //@TODO for ...
    //private VelocityPass m_velocityPass;
    RenderTargetHandle m_DepthTexture;
    private bool pastprocessing = false;
    private bool TAAEnable = false;
    public VForwardRenderer()
    {

        
        
        m_ShadowCasterPass = new VShadowCasterPass(RenderPassEvent.BeforeRenderingShadows);
        m_DepthOnlyPass = new VDepthOnlyPass(RenderPassEvent.BeforeRenderingShadows);
        
        m_DrawObjectPass = new VDrawObjectPass(RenderPassEvent.BeforeRenderingOpaques);
        
        m_DepthTexture.Init("_VCameraDepthTexture");
        
        m_DrawSkyboxPass = new VDrawSkyboxPass(RenderPassEvent.BeforeRenderingSkybox);
        m_blitPass = new VBlitPass(RenderPassEvent.BeforeRenderingOpaques);
        m_TAAPass = new VTAAPass(RenderPassEvent.BeforeRenderingPostProcessing);
        //m_velocityPass = new VelocityPass(RenderPassEvent.BeforeRenderingPostProcessing);
        TAAEnable = true;
    }

    public override void Setup(ScriptableRenderContext context, ref VRenderingData renderingData)
    {
        
        m_ForwardLights.Setup(context,ref renderingData);
        
        ref VCameraData cameraData = ref renderingData.cameraData;
        pastprocessing = renderingData.postProcessingEnabled;
        //temp
       
        
        Camera camera = cameraData.camera;
        RenderTextureDescriptor cameraTargetDescriptor = cameraData.cameraTargetDescriptor;
        
      
        m_ShadowCasterPass.Setup(ref renderingData);
        EnqueuePass(m_ShadowCasterPass);
        
        EnqueuePass(m_DrawObjectPass);
        
        //m_velocityPass.Setup(context,ref renderingData);
        //EnqueuePass(m_velocityPass);
        TAAEnable = renderingData.postFXSettings.TAA.Enable;
        if (TAAEnable)
        {
            m_TAAPass.SetUp(context,ref renderingData);
            EnqueuePass(m_TAAPass);
        }
        
        
        if (pastprocessing)
        {
            m_blitPass.Setup(cameraTargetDescriptor,renderingData.postFXSettings);
            
            EnqueuePass(m_blitPass);
           
        }
      
        
    }
    public override void Dispose()
    {
        if (pastprocessing)
        {
            var cmd = CommandBufferPool.Get();
            cmd.ReleaseTemporaryRT(frameBufferId);
            cmd.ReleaseTemporaryRT(tempBufferId);
            cmd.ReleaseTemporaryRT(DepthBufferId);
            CommandBufferPool.Release(cmd);
        }
        m_ActiveRenderPassQueue.Clear();
    }

    private void PostProcessing(ref CommandBuffer cmd,ref RenderTextureDescriptor cameraTargetDescriptor)
    {
        RenderTextureDescriptor renderTextureDescriptor = new RenderTextureDescriptor
            (cameraTargetDescriptor.width,cameraTargetDescriptor.height
            
            );
        renderTextureDescriptor.depthBufferBits = 16;
        renderTextureDescriptor.sRGB = true;
        renderTextureDescriptor.colorFormat = RenderTextureFormat.ARGB32;
        renderTextureDescriptor.msaaSamples = cameraTargetDescriptor.msaaSamples;
        renderTextureDescriptor.enableRandomWrite = false;
        cmd.GetTemporaryRT(frameBufferId,cameraTargetDescriptor.width, cameraTargetDescriptor.height, 0,
            FilterMode.Bilinear,
            renderTextureDescriptor.colorFormat
            );
        cmd.GetTemporaryRT(tempBufferId,cameraTargetDescriptor.width, cameraTargetDescriptor.height, 0,
            FilterMode.Bilinear);
        cmd.GetTemporaryRT(DepthBufferId,cameraTargetDescriptor.width, cameraTargetDescriptor.height, 24,
            FilterMode.Point,
            RenderTextureFormat.Depth
        );
       
            
            
        m_ActiveCameraColorAttachment = frameBufferId;
        m_ActiveCameraDepthAttachment = DepthBufferId;
        m_tempAttachment = tempBufferId;
        m_blitPass.Config(m_ActiveCameraColorAttachment,m_tempAttachment);
        cmd.SetRenderTarget(m_ActiveCameraColorAttachment, 
            RenderBufferLoadAction.DontCare,RenderBufferStoreAction.StoreAndResolve,
            m_ActiveCameraDepthAttachment,RenderBufferLoadAction.DontCare,RenderBufferStoreAction.StoreAndResolve
        );
        // cmd.SetRenderTarget(m_ActiveCameraColorAttachment, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store,//关于color的设置
        //     m_ActiveCameraDepthAttachment, RenderBufferLoadAction.DontCare, RenderBufferStoreAction.Store //关于depth的设置
        // );
    }
    public override void Execute(ScriptableRenderContext context, ref VRenderingData renderingData)
    {
        ref var cameraData = ref renderingData.cameraData;
        Camera camera = cameraData.camera;
        RenderTextureDescriptor cameraTargetDescriptor = cameraData.cameraTargetDescriptor;
        Debug.Log("CustomScriptableRenderer Execute, pass queue count: " + m_ActiveRenderPassQueue.Count);
        {
            CommandBuffer cmd = CommandBufferPool.Get();
            
            //cmd.ClearRenderTarget(true,true,Color.black,1);
            
            foreach (var pass in m_ActiveRenderPassQueue)
            {
                pass.OnCameraSetup(cmd,ref renderingData);
            }
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
            // Sort the render pass queue
            // SortStable(m_ActiveRenderPassQueue);
        }
        {
            //using var renderBlocks = new RenderBlocks(m_ActiveRenderPassQueue);
            //ready to Execute
        }
        {
            //config renderTarget
            
            CommandBuffer cmd = CommandBufferPool.Get();
            
            
            
            foreach (var pass in m_ActiveRenderPassQueue)
            {
                
                pass.Configure(cmd,cameraData.cameraTargetDescriptor);
                //SetRenderPassAttachments(cmd, pass,ref cameraData);
            }
            
            context.ExecuteCommandBuffer(cmd);
            
            CommandBufferPool.Release(cmd);
        }
        {
            

            m_ShadowCasterPass.Execute(context, ref renderingData);
            
            
            CommandBuffer cmd = CommandBufferPool.Get();

            Matrix4x4 proj;
            Matrix4x4 view= camera.worldToCameraMatrix;
            if (TAAEnable)
            {
                proj = renderingData.JitteredProjectionMatrix;
            }
            else
            {
                proj = camera.projectionMatrix;
            }

            cmd.SetViewport(new Rect(0,0,camera.pixelWidth,camera.pixelHeight));
            
            
            
            cmd.SetViewProjectionMatrices(view,proj);
            
            if (true)
            {
                Matrix4x4 gpuProjectionMatrix = proj;
                //Matrix4x4 viewAndProjectionMatrix = gpuProjectionMatrix * view;
                Matrix4x4 inverseViewMatrix = Matrix4x4.Inverse(view);
                Matrix4x4 inverseProjectionMatrix = Matrix4x4.Inverse(gpuProjectionMatrix);
                Matrix4x4 inverseViewProjection = inverseViewMatrix * inverseProjectionMatrix;
                
                renderingData.cameraData.Inv_Projection=(gpuProjectionMatrix).inverse;
                
                Matrix4x4 worldToCameraMatrix = Matrix4x4.Scale(new Vector3(1.0f, 1.0f, -1.0f)) * view;
                Matrix4x4 cameraToWorldMatrix = worldToCameraMatrix.inverse;
                cmd.SetGlobalMatrix(ShaderPropertyId.worldToCameraMatrix, worldToCameraMatrix);
                cmd.SetGlobalMatrix(ShaderPropertyId.cameraToWorldMatrix, cameraToWorldMatrix);

                cmd.SetGlobalMatrix(ShaderPropertyId.inverseViewMatrix, inverseViewMatrix);
                cmd.SetGlobalMatrix(ShaderPropertyId.inverseProjectionMatrix, inverseProjectionMatrix);
                cmd.SetGlobalMatrix(ShaderPropertyId.inverseViewAndProjectionMatrix, inverseViewProjection);
            }
            if (pastprocessing)
            {
                PostProcessing(ref cmd,ref cameraTargetDescriptor);
            }
            
            
            context.ExecuteCommandBuffer(cmd);
            cmd.Clear();
            
            
            
            
            
            m_DrawObjectPass.Execute(context, ref renderingData);
            
            m_DrawSkyboxPass.Execute(context, ref renderingData);
            
            
            // cmd.SetViewport(new Rect(0,0,camera.pixelWidth,camera.pixelHeight));
            // cmd.SetViewProjectionMatrices(view,proj);
            // context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);


            if (TAAEnable)
            {
                m_TAAPass.Config(m_ActiveCameraColorAttachment,m_ActiveCameraColorAttachment,m_ActiveCameraDepthAttachment);
                m_TAAPass.Execute(context,ref renderingData);
            }
            
            
            
            if (pastprocessing)
            {
                m_blitPass.Execute(context, ref renderingData);
            }
            
        
            
        }
        
     

        {
            CommandBuffer cmd = CommandBufferPool.Get();
            foreach (var pass in m_ActiveRenderPassQueue)
            {
                pass.OnCameraCleanup(cmd);
            }
            
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
            
        }
        
    }
    internal static void SortStable(List<VScriptableRenderPass> list)
    {
        int j;
        for (int i = 1; i < list.Count; ++i)
        {
            VScriptableRenderPass curr = list[i];

            j = i - 1;
            for (; j >= 0 && curr < list[j]; --j)
                list[j + 1] = list[j];

            list[j + 1] = curr;
        }
    }
    internal struct VRenderBlocks : IDisposable
{
    private NativeArray<RenderPassEvent> m_BlockEventLimits;
    private NativeArray<int> m_BlockRanges;
    private NativeArray<int> m_BlockRangeLengths;
    public VRenderBlocks(List<VScriptableRenderPass> activeRenderPassQueue)
    {
        // Upper limits for each block. Each block will contains render passes with events below the limit.
        m_BlockEventLimits = new NativeArray<RenderPassEvent>(4, Allocator.Temp);
        m_BlockRanges = new NativeArray<int>(m_BlockEventLimits.Length + 1, Allocator.Temp);
        m_BlockRangeLengths = new NativeArray<int>(m_BlockRanges.Length, Allocator.Temp);

        

        // blockRanges[0] is always 0
        // blockRanges[i] is the index of the first RenderPass found in m_ActiveRenderPassQueue that has a ScriptableRenderPass.renderPassEvent higher than blockEventLimits[i] (i.e, should be executed after blockEventLimits[i])
        // blockRanges[blockEventLimits.Length] is m_ActiveRenderPassQueue.Count
        FillBlockRanges(activeRenderPassQueue);
        m_BlockEventLimits.Dispose();

        for (int i = 0; i < m_BlockRanges.Length - 1; i++)
        {
            m_BlockRangeLengths[i] = m_BlockRanges[i + 1] - m_BlockRanges[i];
        }
    }

    //  RAII like Dispose pattern implementation for 'using' keyword
    public void Dispose()
    {
        m_BlockRangeLengths.Dispose();
        m_BlockRanges.Dispose();
    }

    // Fill in render pass indices for each block. End index is startIndex + 1.
    void FillBlockRanges(List<VScriptableRenderPass> activeRenderPassQueue)
    {
        int currRangeIndex = 0;
        int currRenderPass = 0;
        m_BlockRanges[currRangeIndex++] = 0;

        // For each block, it finds the first render pass index that has an event
        // higher than the block limit.
        for (int i = 0; i < m_BlockEventLimits.Length - 1; ++i)
        {
            while (currRenderPass < activeRenderPassQueue.Count &&
                   activeRenderPassQueue[currRenderPass].m_RenderPassEvent < m_BlockEventLimits[i])
                currRenderPass++;

            m_BlockRanges[currRangeIndex++] = currRenderPass;
        }

        m_BlockRanges[currRangeIndex] = activeRenderPassQueue.Count;
    }

    public int GetLength(int index)
    {
        return m_BlockRangeLengths[index];
    }

    // Minimal foreach support
    public struct BlockRange : IDisposable
    {
        int m_Current;
        int m_End;
        public BlockRange(int begin, int end)
        {
            //Assert.IsTrue(begin <= end);
            m_Current = begin < end ? begin : end;
            m_End = end >= begin ? end : begin;
            m_Current -= 1;
        }

        public BlockRange GetEnumerator() { return this; }
        public bool MoveNext() { return ++m_Current < m_End; }
        public int Current { get => m_Current; }
        public void Dispose() { }
    }

    public BlockRange GetRange(int index)
    {
        return new BlockRange(m_BlockRanges[index], m_BlockRanges[index + 1]);
    }
}
}


